package com.game.rpc;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.game.bean.User;
import com.game.json.JSONObject;
import com.game.share.StreamReader;
import com.game.share.StreamWriter;

public class ClassInfo {
	private static final int T_INT = 1;
	private static final int T_LONG = 2;
	private static final int T_DOUBLE = 3;
	private static final int T_STRING = 4;
	private static final int T_ARRAY = 5;
	private static final int T_OBJECT = 6;

	BeanProperty[] propretyList;
	Class<?> clsName;
	Method[] setMethodList;
	Method[] getMethodList;

	private static final HashMap<String, ClassInfo> INFO_MAP = new HashMap<String, ClassInfo>();

	public static void init() {
		try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			factory.setIgnoringElementContentWhitespace(true);
			DocumentBuilder db = factory.newDocumentBuilder();
			Document doc = db.parse(new FileInputStream("res/info.xml"));
			NodeList nodeList = doc.getElementsByTagName("class");
			System.out.println("len : " + nodeList.getLength());
			for (int i = 0; i < nodeList.getLength(); i++) {
				Node node = nodeList.item(i);
				Node nameNode = node.getAttributes().getNamedItem("name");
				String className = "com.game.bean." + nameNode.getNodeValue();
				System.out.println("class name : " + className);
				ClassInfo info = new ClassInfo();
				info.clsName = Class.forName(className);
				NodeList children = node.getChildNodes();
				info.propretyList = new BeanProperty[children.getLength()];
				info.setMethodList = new Method[children.getLength()];
				info.getMethodList = new Method[children.getLength()];
				INFO_MAP.put(className, info);
				for (int j = 0; j < children.getLength(); j++) {
					Node fieldNode = children.item(j);
					if (fieldNode.getAttributes() != null) {
						Node fieldName = fieldNode.getAttributes()
								.getNamedItem("name");
						String type1 = fieldNode.getAttributes()
								.getNamedItem("type1").getNodeValue();
						String type2 = fieldNode.getAttributes()
								.getNamedItem("type2").getNodeValue();
						System.out.println("field : "
								+ fieldName.getNodeValue());
						info.propretyList[j] = new BeanProperty(
								fieldName.getNodeValue(),
								getType(type1, type2), isArray(type1));
						info.setMethodList[i] = info.clsName.getMethod(
								"set" + fieldName.getNodeValue(),
								getClassByType(getType(type1, type2),
										isArray(type1)));
						info.setMethodList[i] = info.clsName.getMethod("get"
								+ fieldName.getNodeValue(), new Class<?>[] {});
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void writeObject(StreamWriter writer, int type, boolean isArray,
			Object value) throws IOException {
		if (isArray) {
			switch (type) {
			case T_INT:
				int[] intArray = (int[]) value;
				writer.writeInt(intArray.length);
				for (int i : intArray) {
					writer.writeInt(i);
				}
				break;
			case T_LONG:
				long[] longArray = (long[]) value;
				writer.writeInt(longArray.length);
				for (long i : longArray) {
					writer.writeLong(i);
				}
				break;
			case T_STRING:
				String[] strArray = (String[]) value;
				writer.writeInt(strArray.length);
				for (String i : strArray) {
					writer.writeString(i);
				}
				break;
			}
		} else {
			switch (type) {
			case T_INT:
				writer.writeInt((Integer) value);
			case T_LONG:
				writer.writeLong((Long) value);
			case T_STRING:
				writer.writeString((String) value);
			}
		}
	}

	private Object readObject(StreamReader reader, int type, boolean isArray)
			throws IOException {
		if (isArray) {
			int len = reader.readInt();
			switch (type) {
			case T_INT:
				int[] intResult = new int[len];
				for (int i = 0; i < len; i++) {
					intResult[i] = reader.readInt();
				}
				return intResult;
			case T_LONG:
				long[] longResult = new long[len];
				for (int i = 0; i < len; i++) {
					longResult[i] = reader.readLong();
				}
				return longResult;
			case T_STRING:
				String[] strResult = new String[len];
				for (int i = 0; i < len; i++) {
					strResult[i] = reader.readString();
				}
				return strResult;
			}
		} else {
			switch (type) {
			case T_INT:
				return reader.readInt();
			case T_LONG:
				return reader.readLong();
			case T_STRING:
				return reader.readString();
			}
		}
		return null;
	}

	public void writeObject(StreamWriter writer, Object obj) {
		for (int i = 0; i < getMethodList.length; i++) {
			try {
				Object value = getMethodList[i].invoke(obj, new Object[] {});
				writeObject(writer, propretyList[i].getType(),
						propretyList[i].isArray(), value);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}

	public Object readObject(StreamReader reader) {
		try {
			Object obj = clsName.newInstance();
			for (int i = 0; i < setMethodList.length; i++) {
				setMethodList[i].invoke(
						obj,
						new Object[] { readObject(reader,
								propretyList[i].getType(),
								propretyList[i].isArray()) });
			}
			return obj;
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	private static int getType(String type) {
		if (type.equals("ARRAY")) {
			return T_ARRAY;
		} else if (type.equals("int")) {
			return T_INT;
		} else if (type.equals("long")) {
			return T_LONG;
		} else if (type.equals("double")) {
			return T_DOUBLE;
		} else if (type.equals("String")) {
			return T_STRING;
		}
		return T_OBJECT;
	}

	private static Class<?> getClassByType(int type, boolean isArray) {
		if (isArray) {
			switch (type) {
			case T_INT:
				return int[].class;
			case T_LONG:
				return long[].class;
			case T_DOUBLE:
				return double[].class;
			case T_STRING:
				return String[].class;
			}
		} else {
			switch (type) {
			case T_INT:
				return int.class;
			case T_LONG:
				return long.class;
			case T_DOUBLE:
				return double.class;
			case T_STRING:
				return String.class;
			}
		}
		return null;
	}

	private static int getType(String type1, String type2) {
		if (isArray(type1)) {
			return getType(type2);
		}
		return getType(type1);
	}

	public static boolean isArray(String type1) {
		return type1.equals("ARRAY");
	}

	public static void main(String[] args) {
		init();
		User user = new User();
		user.setId(12345);
		user.setName("userName");
		JSONObject object = new JSONObject(user);
		System.out.println(object.toString());
	}

	public static ClassInfo getInfo(String className) {
		return INFO_MAP.get(className);
	}
}
